﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Demo.Api.Data;
using Demo.Api.Entities;
using Demo.Api.Models;
using KmFlow;
using Microsoft.EntityFrameworkCore;

namespace Demo.Api.Services
{
    public class AffairObjectRepository : Repository<AffairObjectDo>
    {
        public AffairObjectRepository(DemoDbContext context) : base(context)
        {
            DataSet = Context.AffairObjects;
        }
        /// <summary>
        /// 获取所有
        /// </summary>
        /// <returns></returns>
        public  Task<List<AffairObjectWithAffair>> Gets2Async()
        {
            return (from ao in DataSet
                    join a in Context.Affairs on ao.AffairId equals a.Id
                    orderby ao.Id descending 
                    select new AffairObjectWithAffair
                    {
                        Id = ao.Id,
                        AffairId = ao.AffairId,
                        FlowChartId = ao.FlowChartId,
                        AffairName = a.Name,
                        Director = ao.Director,
                        Manager = ao.Manager,
                        Name = ao.Name,
                        Num = ao.Num
                    }).ToListAsync();
        }

        public Task<List<AffairObjectWithAffair>> Gets2Async(long affairId)
        {
            return (from ao in DataSet.Where(x=>x.AffairId==affairId)
                join a in Context.Affairs on ao.AffairId equals a.Id
                orderby ao.Id descending
                select new AffairObjectWithAffair
                {
                    Id = ao.Id,
                    AffairId = ao.AffairId,
                    FlowChartId = ao.FlowChartId,
                    AffairName = a.Name,
                    Director = ao.Director,
                    Manager = ao.Manager,
                    Name = ao.Name,
                    Num = ao.Num
                }).ToListAsync();
        }

        public override Task<bool> Add(AffairObjectDo data)
        {
            var nodes = Context.Nodes.Where(x => x.FlowChartId == data.FlowChartId).Select(x => x.ToNode()).ToArray();
            var lines = Context.TabLines.Where(x => x.FlowChartId == data.FlowChartId).Select(x => x.ToLine())
                .ToArray();
            var flowChart = new FlowChart(nodes, lines);
            var process = new Process(flowChart);
            process.Load();
            try
            {
                var firstNode = nodes.First(x => x.NodeType == NodeType.Start);
                var startLine = lines.First(x => x.TailNodeId == firstNode.Id);
                process.Run(startLine.Id);
                if (process.Current != null)
                {
                    foreach (var node in process.Current)
                        Context.FlowChartProcesses.Add(new FlowChartProcessDo
                        {
                            Id = IdWorker.NextId(),
                            NodeType = (int) node.NodeType,
                            RuleId = node.RuleId,
                            AffairProcessId = data.Id,
                            FlowChartId = data.FlowChartId,
                            NodeId = node.Id,
                            AffairId = data.AffairId,
                            OperatingNodeType = (int) node.OperatingNodeType,
                            OperatingTime = DateTime.Now
                        });
                    Context.SaveChanges();
                }
                
                if (process.Result != null)
                {
                    foreach (var node in process.Result)
                    {
                        Context.FlowChartProcesses.Add(new FlowChartProcessDo
                        {
                            Id = IdWorker.NextId(),
                            NodeType = (int)node.NodeType,
                            RuleId = node.RuleId,
                            AffairProcessId = data.Id,
                            FlowChartId = data.FlowChartId,
                            NodeId = node.Id,
                            AffairId = data.AffairId,
                            OperatingNodeType = (int)node.OperatingNodeType,
                            OperatingTime = DateTime.Now
                        });
                    }

                    Context.SaveChanges();
                }

                if (process.PassedLines != null)
                {
                    foreach (var line in process.PassedLines)
                        Context.ProcessLines.Add(new ProcessLineDo
                        {
                            Id = IdWorker.NextId(),
                            LineId = line.Id,
                            AffairProcessId = data.Id,
                            OperatingTime = DateTime.Now
                        });
                    Context.SaveChanges();
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }

            return base.Add(data);
        }

        public bool Update(AffairObjectUpdateDto data)
        {
            var baseData = Context.AffairObjects.First(x => x.Id == data.ObjectId);
            var currentNode = (from p in Context.FlowChartProcesses
                join n in Context.Nodes on p.NodeId equals n.Id
                where p.AffairProcessId == data.ObjectId && p.OperatingNodeType == (int) OperatingNodeType.Working
                select new ResultNode
                {
                    Id = p.Id,
                    NodeType = (NodeType) p.NodeType,
                    OperatingNodeType = (OperatingNodeType) p.OperatingNodeType,
                    OperatingTime = p.OperatingTime,
                    RuleId = p.RuleId,
                    SeparateNodeId = n.SeparateNodeId
                }).ToList();

            var nodes = Context.Nodes.Where(x => x.FlowChartId == baseData.FlowChartId).Select(x => x.ToNode()).ToArray();
            var lines = Context.TabLines.Where(x => x.FlowChartId == baseData.FlowChartId).Select(x => x.ToLine())
                .ToArray();
            var flowChart = new FlowChart(nodes, lines);
            var process = new Process(flowChart);
            process.Load();
            try
            {
                process.Run(currentNode, data.LineId);
                if (process.Current != null)
                {
                    foreach (var node in process.Current)
                        Context.FlowChartProcesses.Add(new FlowChartProcessDo
                        {
                            Id = IdWorker.NextId(),
                            NodeType = (int) node.NodeType,
                            RuleId = node.RuleId,
                            AffairProcessId = data.ObjectId,
                            FlowChartId = baseData.FlowChartId,
                            NodeId = node.Id,
                            AffairId = baseData.AffairId,
                            OperatingNodeType = (int) node.OperatingNodeType,
                            OperatingTime = DateTime.Now
                        });
                    Context.SaveChanges();
                }

                if (process.Result != null)
                {
                    foreach (var node in process.Result)
                    {
                        var temp = Context.FlowChartProcesses.FirstOrDefault(x =>
                            x.AffairProcessId == data.ObjectId && x.NodeId == node.Id);
                        if (temp != null)
                        {
                            temp.OperatingNodeType = (int) node.OperatingNodeType;
                            temp.OperatingTime = DateTime.Now;
                        }
                    }

                    Context.SaveChanges();
                }

                if (process.PassedLines != null)
                {
                    foreach (var line in process.PassedLines)
                        Context.ProcessLines.Add(new ProcessLineDo
                        {
                            Id = IdWorker.NextId(),
                            LineId = line.Id,
                            AffairProcessId = data.ObjectId,
                            OperatingTime = DateTime.Now
                        });
                    Context.SaveChanges();
                }

                return true;
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
                return false;
            }
        }
        //public bool Update2(AffairObjectUpdateDto data)
        //{
        //    var baseData = Context.AffairObjects.First(x => x.Id == data.RelatedId);
        //    var currentNode = (from p in Context.FlowChartProcesses
        //                       join n in Context.Nodes on p.NodeId equals n.Id
        //                       where p.AffairProcessId == data.RelatedId && p.OperatingNodeType == (int)OperatingNodeType.Working
        //                       select new ResultNode
        //                       {
        //                           Id = p.Id,
        //                           NodeType = (NodeType)p.NodeType,
        //                           OperatingNodeType = (OperatingNodeType)p.OperatingNodeType,
        //                           OperatingTime = p.OperatingTime,
        //                           RuleId = p.RuleId,
        //                           SeparateNodeId = n.SeparateNodeId
        //                       }).ToList();

        //    var nodes = Context.Nodes.Where(x => x.FlowChartId == data.FlowChartId).Select(x => x.ToNode()).ToArray();
        //    var lines = Context.TabLines.Where(x => x.FlowChartId == data.FlowChartId).Select(x => x.ToLine())
        //        .ToArray();
        //    var flowChart = new FlowChart(nodes, lines);
        //    var process = new Process(flowChart);
        //    //flowChart.Load(new AutoEngine<AffairProcessDo>(new DemoRuleProvider(), baseData), nodes, lines);
        //    try
        //    {
        //        process.Run(currentNode, data.LineId);
        //        if (process.Current != null)
        //        {
        //            foreach (var node in process.Current)
        //                Context.FlowChartProcesses.Add(new FlowChartProcessDo
        //                {
        //                    Id = IdWorker.NextId(),
        //                    NodeType = (int)node.NodeType,
        //                    RuleId = node.RuleId,
        //                    AffairProcessId = data.RelatedId,
        //                    FlowChartId = data.FlowChartId,
        //                    NodeId = node.Id,
        //                    AffairId = baseData.AffairId,
        //                    OperatingNodeType = (int)node.OperatingNodeType,
        //                    OperatingTime = DateTime.Now
        //                });
        //            Context.SaveChanges();
        //        }

        //        if (process.Result != null)
        //        {
        //            foreach (var node in process.Result)
        //            {
        //                var temp = Context.FlowChartProcesses.FirstOrDefault(x =>
        //                    x.AffairProcessId == data.RelatedId && x.NodeId == node.Id);
        //                if (temp != null)
        //                {
        //                    temp.OperatingNodeType = (int)node.OperatingNodeType;
        //                    temp.OperatingTime = DateTime.Now;
        //                }
        //            }

        //            Context.SaveChanges();
        //        }

        //        if (process.PassedLines != null)
        //        {
        //            foreach (var line in process.PassedLines)
        //                Context.ProcessLines.Add(new ProcessLineDo
        //                {
        //                    Id = IdWorker.NextId(),
        //                    LineId = line.Id,
        //                    AffairProcessId = data.RelatedId,
        //                    OperatingTime = DateTime.Now
        //                });
        //            Context.SaveChanges();
        //        }

        //        return true;
        //    }
        //    catch (Exception e)
        //    {
        //        Console.WriteLine(e.ToString());
        //        return false;
        //    }
        //}

        /// <summary>
        ///     获取事务实例的流程图
        /// </summary>
        /// <param name="id">事务实例的Id</param>
        /// <param name="flowId">关联流程的Id</param>
        /// <returns></returns>
        public async Task<FlowChartProcessNodeAndLineDto> GetProcessChart(long id, long flowId)
        {
            var result = new FlowChartProcessNodeAndLineDto();
            result.Lines = (from n in Context.TabLines
                join p in Context.ProcessLines.Where(x => x.AffairProcessId == id) on n.Id equals p.LineId into temp
                from t in temp.DefaultIfEmpty()
                where n.FlowChartId == flowId
                select new FlowChartProcessLineDto(n)
                {
                    Passed = t != null
                }).ToList();
            result.Nodes = (from n in Context.Nodes
                join p in Context.FlowChartProcesses.Where(x => x.AffairProcessId == id) on n.Id equals p.NodeId into
                    temp
                from t in temp.DefaultIfEmpty()
                where n.FlowChartId == flowId
                select new FlowChartProcessNode(n, t)).ToList();
            return await Task.FromResult(result);
        }

        /// <summary>
        ///     获取当前节点下面的
        /// </summary>
        /// <param name="id">事务实例Id</param>
        /// <param name="flowId">流程Id</param>
        /// <returns></returns>
        public Task<IQueryable<FlowChartProcessLineDto>> GetCurrentOptions(long id, long flowId)
        {
            return Task.FromResult(from i in Context.TabLines
                join p in Context.FlowChartProcesses on i.TailNodeId equals p.NodeId
                join n in Context.Nodes on p.NodeId equals n.Id
                where i.FlowChartId == flowId && p.AffairProcessId == id &&
                      p.OperatingNodeType == (int) OperatingNodeType.Working
                select new FlowChartProcessLineDto(i, n.Name));
        }
        /// <summary>
        /// 获取当前的操作
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public  Task<IQueryable<LineDo>> GetCurrentOptions(long id)
        {
           return Task.FromResult(from p in Context.FlowChartProcesses.Where(x => x.OperatingNodeType == (int)OperatingNodeType.Working)
               join l in Context.TabLines on p.NodeId equals l.TailNodeId
               where p.AffairProcessId == id
               select l);
        }
    }

    public class AffairObjectWithAffair : AffairObjectDo
    {
        /// <summary>
        ///     事项名称
        /// </summary>
        public string AffairName { get; set; }
    }
}